#include "dm6_protocol.h"
#include "log.h"

namespace secsmart_protocol
{
    DETECT_RESULT DM6Protocol::parse(secsmart_session::session_sptr session, const uint8_t *buf, size_t size, bool is_forward)
    {
        direction = secsmart_session::session_dirction_t::SESSION_DIRCTION_UNKNOW;
        if(size < DM6_PACKET_HEADLEN)
        {
            return DETECT_RESULT::DETECT_UNKNOWN; //小于协议头
        }

        uint16_t cmdType = (uint16_t)(((buf[5] & 0xFF) << 8) | (buf[4] & 0xFF));
        if(!isTrueCmd(cmdType))
        {
            return DETECT_RESULT::DETECT_UNKNOWN;  //可能存在TCP分段的情况
        }

        uint64_t pBodyLen = (uint32_t)(((buf[9] & 0xFF) << 24) | ((buf[8] & 0xFF) << 16) | ((buf[7] & 0xFF) << 8) | (buf[6] & 0xFF));

        if(pBodyLen < DM6_FIX_MSG_LEN - DM6_PACKET_HEADLEN)
        {
            if((size - 64) == (DM6_FIX_MSG_LEN - DM6_PACKET_HEADLEN) || (pBodyLen == size - 64))
            {
                if(!setIpPort(session,getDirction(),is_forward))
                {
                    LOG_DEBUG("set ip port fail");
                    return DETECT_RESULT::DETECT_FAIL;
                }
                if(cmdType == RET_TYPE::RET_STARTUP)
                {
                    // 消息类型为RET_STARTUP时提取数据库版本信息
                    size_t position = 64;  // 从消息体开始解析
                    parseVersion(buf,position,size);
                }
                addMatchCount();
                return DETECT_RESULT::DETECT_OK;
            }
            else
            {
                addNoMatchCount();
                return DETECT_RESULT::DETECT_FAIL;
            }
        }
        else
        {
            if(pBodyLen == size - 64)
            {
                if(!setIpPort(session,getDirction(),is_forward))
                {
                    LOG_DEBUG("set ip port fail");
                    return DETECT_RESULT::DETECT_FAIL;
                }
                if(cmdType == RET_TYPE::RET_STARTUP)
                {
                    size_t position = 64;
                    parseVersion(buf,position,size);
                }
                addMatchCount();
                return DETECT_RESULT::DETECT_OK;
            }
            else 
            {
                addNoMatchCount();
                return DETECT_RESULT::DETECT_FAIL;
            }
        }
        
    }

    bool DM6Protocol::isTrueCmd(const uint16_t cmdType)
    {       
        // 存在个别请求消息类型与返回消息类型的枚举值相同，对于这种情况无法识别方向，因此不识别
        switch(cmdType)
        {
            case CMD_TYPE::CMD_LOGIN:
            case CMD_TYPE::CMD_LOGOUT:
            case CMD_TYPE::CMD_STMT_ALLOCATE:
            case CMD_TYPE::CMD_STMT_ALLOCATE_AUTO:
            case CMD_TYPE::CMD_STMT_FREE:
            case CMD_TYPE::CMD_STMT_FREE_AUTO:
            case CMD_TYPE::CMD_PREPARE:
            case CMD_TYPE::CMD_PREPARE_AUTO:
            case CMD_TYPE::CMD_EXECUTE:
            case CMD_TYPE::CMD_EXECUTE_AUTO:
            case CMD_TYPE::CMD_FETCH:
            case CMD_TYPE::CMD_FETCH_AUTO:
            case CMD_TYPE::CMD_COMMIT:
            case CMD_TYPE::CMD_ROLLBACK:
            case CMD_TYPE::CMD_GET_SQLSATE:
            case CMD_TYPE::CMD_CANCLE:
            case CMD_TYPE::CMD_POSITION:
            case CMD_TYPE::CMD_UNPREPARE:
            case CMD_TYPE::CMD_PUT_DATA:
            case CMD_TYPE::CMD_PUT_DATA_AUTO:
            case CMD_TYPE::CMD_GET_DATA:
            case CMD_TYPE::CMD_GET_DATA_AUTO:
            case CMD_TYPE::CMD_CREATE_BLOB:
            case CMD_TYPE::CMD_CREATE_BLOB_AUTO:
            case CMD_TYPE::CMD_CLOSE_STMT:
            case CMD_TYPE::CMD_CLOSE_STMT_AUTO:
            case CMD_TYPE::CMD_TIME_OUT:
            case CMD_TYPE::CMD_CURSOR_PREPARE:
            case CMD_TYPE::CMD_CURSOR_EXECUTE:
            case CMD_TYPE::CMD_EXPLAIN:
            case CMD_TYPE::CMD_PLAN_START:
            case CMD_TYPE::CMD_PLAN_START_AUTO:
            case CMD_TYPE::CMD_PLAN_ADD_ERRCODES:
            case CMD_TYPE::CMD_PLAN_ADD_ERRCODES_AUTO:
            case CMD_TYPE::CMD_PLAN_DEL_ERRCODES:
            case CMD_TYPE::CMD_PLAN_DEL_ERRCODES_AUTO:
            case CMD_TYPE::CMD_PUT_SUBSTR:
            case CMD_TYPE::CMD_GET_SUBSTR:
            case CMD_TYPE::CMD_CURSOR_SET_NAME:
            case CMD_TYPE::CMD_CURSOR_SET_NAME_AUTO:
            case CMD_TYPE::CMD_GET_ROW_NUM:
            case CMD_TYPE::CMD_BLOB_SET_BYTES:
            case CMD_TYPE::CMD_TEXT_GET_LEN:
            case CMD_TYPE::CMD_TEXT_GET_SUB_STR:
            case CMD_TYPE::CMD_TEXT_SET_STR:
            case CMD_TYPE::CMD_BLOB_TRUNCATE:
            case CMD_TYPE::CMD_TEXT_TRUNCATE:
            case CMD_TYPE::CMD_BLOB_GET_LEN:
            case CMD_TYPE::CMD_BLOB_GET_SUB_STR:
            case CMD_TYPE::CMD_BULK_INSERT:
            case CMD_TYPE::CMD_DUPLICATE_BULK_INSERT:
            case CMD_TYPE::CMD_DESCRIBE_DTYPE:
            case CMD_TYPE::CMD_MORE_RESULT:
            case CMD_TYPE::CMD_MORE_RESULT_AUTO:
            case CMD_TYPE::CMD_PREPARE_EX:
            case CMD_TYPE::CMD_PREPARE_EX_AUTO:
            case CMD_TYPE::CMD_SET_BCP:
            case CMD_TYPE::CMD_BCP_INSERT:
            case CMD_TYPE::CMD_CLR_BCP:
            case CMD_TYPE::CMD_RESET_SESS:
            case CMD_TYPE::CMD_BLOB_SET_BYTES_NEW:
            case CMD_TYPE::CMD_BLOB_SET_BYTES_NEW_AUTO:
            case CMD_TYPE::CMD_TEXT_GET_LEN_NEW:
            case CMD_TYPE::CMD_TEXT_GET_LEN_NEW_AUTO:
            case CMD_TYPE::CMD_TEXT_GET_SUB_STR_NEW:
            case CMD_TYPE::CMD_TEXT_GET_SUB_STR_NEW_AUTO:
            case CMD_TYPE::CMD_TEXT_SET_STR_NEW:
            case CMD_TYPE::CMD_TEXT_SET_STR_NEW_AUTO:
            case CMD_TYPE::CMD_BLOB_TRUNCATE_NEW:
            case CMD_TYPE::CMD_BLOB_TRUNCATE_NEW_AUTO:
            case CMD_TYPE::CMD_TEXT_TRUNCATE_NEW:
            case CMD_TYPE::CMD_TEXT_TRUNCATE_NEW_AUTO:
            case CMD_TYPE::CMD_BLOB_GET_LEN_NEW:
            case CMD_TYPE::CMD_BLOB_GET_LEN_NEW_AUTO:
            case CMD_TYPE::CMD_BLOB_GET_SUB_STR_NEW:
            case CMD_TYPE::CMD_BLOB_GET_SUB_STR_NEW_AUTO:
            case CMD_TYPE::CMD_XA:
            case CMD_TYPE::CMD_CHK_TRX:
            case CMD_TYPE::CMD_SYNC_SUSP_TRX:
            case CMD_TYPE::CMD_SYNC_HEUR_COMMIT:
            case CMD_TYPE::CMD_SYNC_HEUR_ROLLBACK:
            case CMD_TYPE::CMD_STARTUP_AUTO:
            case CMD_TYPE::CMD_SET_APP_NAME:
            case CMD_TYPE::CMD_SET_COMPRESS:
            {
                LOG_DEBUG("DM6 Request packet detected !")
                direction = secsmart_session::session_dirction_t::SESSION_DIRCTION_REQUEST;
                return true;
            }   
            case RET_TYPE::RET_BASE:    
            case RET_TYPE::RET_DDL_CDB:    
            case RET_TYPE::RET_DDL_CTAB:    
            case RET_TYPE::RET_DDL_DTAB:    
            case RET_TYPE::RET_DDL_CVIEW:    
            case RET_TYPE::RET_DDL_DVIEW:    
            case RET_TYPE::RET_DDL_CIND:    
            case RET_TYPE::RET_DDL_DIND:    
            case RET_TYPE::RET_DDL_CUSR:    
            case RET_TYPE::RET_DDL_DUSR:    
            case RET_TYPE::RET_DDL_CROL:    
            case RET_TYPE::RET_DDL_DROL:    
            case RET_TYPE::RET_DDL_DROP:    
            case RET_TYPE::RET_DDL_ALTDB:    
            case RET_TYPE::RET_DDL_ALTUSR:    
            case RET_TYPE::RET_DDL_CFUNC:    
            case RET_TYPE::RET_DDL_CPROC:    
            case RET_TYPE::RET_DDL_GRANT:    
            case RET_TYPE::RET_DDL_REVOKE:    
            case RET_TYPE::RET_DDL_ALTTAB:    
            case RET_TYPE::RET_COMMIT:    
            case RET_TYPE::RET_ROLLBACK:    
            case RET_TYPE::RET_EXPLAIN:    
            case RET_TYPE::RET_SET_TRAN:    
            case RET_TYPE::RET_SAVEPNT:    
            case RET_TYPE::RET_SET_CURDB:    
            case RET_TYPE::RET_SET_CURSCH:    
            case RET_TYPE::RET_LOCK_TAB:    
            case RET_TYPE::RET_DDL_AUDIT:    
            case RET_TYPE::RET_DML_INSERT:    
            case RET_TYPE::RET_DML_DELETE:    
            case RET_TYPE::RET_DML_UPDATE:    
            case RET_TYPE::RET_DML_SELECT:    
            case RET_TYPE::RET_DML_SELECT_INTO:    
            case RET_TYPE::RET_DLM_CALL:    
            case RET_TYPE::RET_LOGIN:    
            case RET_TYPE::RET_NEED_DATA:    
            case RET_TYPE::RET_SET_SESS_TRAN:    
            case RET_TYPE::RET_FETCH_NOT_OVER:    
            case RET_TYPE::RET_EXEC_PROC:    
            case RET_TYPE::RET_SIMPLE:    
            case RET_TYPE::RET_DDL_CSCHEMA:    
            case RET_TYPE::RET_PLAN_ERRCODE:    
            case RET_TYPE::RET_DDL_ALTTRIG:    
            case RET_TYPE::RET_DDL_DROP_TABLE:    
            case RET_TYPE::RET_DDL_DROP_INDEX:    
            case RET_TYPE::RET_DDL_DROP_VIEW:    
            case RET_TYPE::RET_DML_TRCT:    
            case RET_TYPE::RET_SET_IDENIINS:    
            case RET_TYPE::RET_DDL_CSEQ:    
            case RET_TYPE::RET_DML_CURSOR_DECLARE:    
            case RET_TYPE::RET_DML_CURSOR_OPEN:    
            case RET_TYPE::RET_DML_CURSOR_CLOSE:       
            case RET_TYPE::RET_DML_CURSOR_DELETE:    
            case RET_TYPE::RET_DML_CURSOR_FETCH:    
            case RET_TYPE::RET_DML_CURSOR_SEEK:    
            case RET_TYPE::RET_DDL_CLGN:    
            case RET_TYPE::RET_DDL_ALTLGN:    
            case RET_TYPE::RET_DDL_CCONIND:    
            case RET_TYPE::RET_DDL_DCONIND:    
            case RET_TYPE::RET_DDL_ALTCONIND:    
            case RET_TYPE::RET_STARTUP:
            {
                LOG_DEBUG("DM6 Response packet detected !");
                direction = secsmart_session::session_dirction_t::SESSION_DIRCTION_RESPONSE;
                return true;
            }

            default:
            {
                return false;
            }
        }
    }

    std::string DM6Protocol::extendInfo()
    {
        return "";
    }

    void DM6Protocol::parseVersion(const uint8_t *buf,size_t &position, size_t size)
    { 
        //此处代码从内网协议解析代码抽取改写而成
        skipBytesWithLen4(buf,position,size);
        skipBytesWithLen4(buf,position,size);
        skipBytesWithLen4(buf,position,size);
        skipBytesWithLen4(buf,position,size);
        uint32_t uiLen = readU32LE(buf,position,size);
        if(256 <= uiLen)
        {
            uiLen = 255;
        }
        skipLen(position,uiLen);
        uint32_t uiFlag = readU32LE(buf,position,size);
        uint32_t uiGlobalServerSeries = readU32LE(buf,position,size);
        versionInfo.clear();
        readBytesWithLen4(buf,position,size,versionInfo);
        if(!versionInfo.empty())
        {
            version = versionInfo;
        }
    }

}   